/******************************************************************************* * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public * License v3.0 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ package cuchaz.enigma.bytecode; import javassist.CtBehavior; import javassist.CtClass; import javassist.bytecode.ByteArray; import javassist.bytecode.CodeAttribute; import javassist.bytecode.ConstPool; import javassist.bytecode.LocalVariableAttribute; import javassist.bytecode.LocalVariableTypeAttribute; import cuchaz.enigma.mapping.ArgumentEntry; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.EntryFactory; import cuchaz.enigma.mapping.Translator; public class LocalVariableRenamer { private Translator m_translator; public LocalVariableRenamer(Translator translator) { m_translator = translator; } public void rename(CtClass c) { for(CtBehavior behavior : c.getDeclaredBehaviors()) { // if there's a local variable table, just rename everything to v1, // v2, v3, ... for now CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); if(codeAttribute == null) continue; BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); ConstPool constants = c.getClassFile().getConstPool(); LocalVariableAttribute table = (LocalVariableAttribute)codeAttribute .getAttribute(LocalVariableAttribute.tag); if(table != null) renameLVT(behaviorEntry, constants, table); LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute)codeAttribute .getAttribute(LocalVariableAttribute.typeTag); if(typeTable != null) renameLVTT(typeTable, table); } } private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table) { // skip empty tables if(table.tableLength() <= 0) return; // where do we start counting variables? int starti = 0; if(table.variableName(0).equals("this")) // skip the "this" variable starti = 1; // rename method arguments first int numArgs = 0; if(behaviorEntry.getSignature() != null) { numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); for(int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { int argi = i - starti; String argName = m_translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); if(argName == null) argName = "a" + (argi + 1); renameVariable(table, i, constants.addUtf8Info(argName)); } } // then rename the rest of the args, if any for(int i = starti + numArgs; i < table.tableLength(); i++) { int firstIndex = table.index(starti + numArgs); renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); } } private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { // rename args to the same names as in the LVT for(int i = 0; i < typeTable.tableLength(); i++) renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); } private void renameVariable(LocalVariableAttribute table, int i, int stringId) { // based off of LocalVariableAttribute.nameIndex() ByteArray.write16bit(stringId, table.get(), i * 10 + 6); } private int getNameIndex(LocalVariableAttribute table, int index) { for(int i = 0; i < table.tableLength(); i++) if(table.index(i) == index) return table.nameIndex(i); return 0; } }